#include "As2VirtualMachine.h"

namespace LightPlayer{

	As2VM::As2VM(){
		init();
	}

	void As2VM::init(){
		m_Stack.clear();
		m_VarPool.clear();
	}

	void As2VM::push_null()					
	{	As2Var* newVar = m_VarPool.getNew();	newVar->set_null();				m_Stack.push_back( newVar );	}

	void As2VM::push_undef()				
	{	As2Var* newVar = m_VarPool.getNew();	newVar->set_undef();			m_Stack.push_back( newVar );	}

	void As2VM::push_str(const char* str)	
	{	As2Var* newVar = m_VarPool.getNew();	newVar->set_str(str);			m_Stack.push_back( newVar );	}

	void As2VM::push_NewStr(const char* str)	
	{	As2Var* newVar = m_VarPool.getNew();	newVar->set_NewStr(str);		m_Stack.push_back( newVar );	}

	void As2VM::push_float(float f)	
	{	As2Var* newVar = m_VarPool.getNew();	newVar->set_float(f);			m_Stack.push_back( newVar );	}

	void As2VM::push_register(uint8 u)	
	{	As2Var* newVar = m_VarPool.getNew();	newVar->set_register(u);		m_Stack.push_back( newVar );	}

	void As2VM::push_bool(bool b)	
	{	As2Var* newVar = m_VarPool.getNew();	newVar->set_bool(b);			m_Stack.push_back( newVar );	}

	void As2VM::push_double(double d)	
	{	As2Var* newVar = m_VarPool.getNew();	newVar->set_double(d);			m_Stack.push_back( newVar );	}

	void As2VM::push_int(int i)	
	{	As2Var* newVar = m_VarPool.getNew();	newVar->set_int(i);				m_Stack.push_back( newVar );	}

	void As2VM::push_constant(uint16 ui)	
	{	As2Var* newVar = m_VarPool.getNew();	newVar->set_constant(ui);		m_Stack.push_back( newVar );	}

	void As2VM::push_As2Var(const As2Var& value)	
	{	As2Var* newVar = m_VarPool.getNew();	(*newVar) = value;				m_Stack.push_back( newVar );	}

	As2Var As2VM::pop(){
		As2Var var_copy;
		if(m_Stack.size() > 0){
			var_copy = *(m_Stack.back());
			m_VarPool.FreeObject( m_Stack.back() );
			m_Stack.pop_back();
		}
		return var_copy;
	}

	// Arithmetic operator

	void As2VM::DoAdd(){
		assert(m_Stack.size() >= 2);
		As2Var Var1 = pop();
		As2Var Var2 = pop();

		float f1 = Var1.getAsFloat();
		float f2 = Var2.getAsFloat();
	
		push_float( f1 + f2 );
	}

	void As2VM::DoSub(){
		assert(m_Stack.size() >= 2);
		As2Var Var1 = pop();
		As2Var Var2 = pop();

		float f1 = Var1.getAsFloat();
		float f2 = Var2.getAsFloat();

		push_float( f2 - f1 );
	}

	void As2VM::DoMult(){
		assert(m_Stack.size() >= 2);
		As2Var Var1 = pop();
		As2Var Var2 = pop();

		float f1 = Var1.getAsFloat();
		float f2 = Var2.getAsFloat();

		push_float( f1 * f2 );
	}

	void As2VM::DoDiv(){
		assert(m_Stack.size() >= 2);
		As2Var Var1 = pop();
		As2Var Var2 = pop();

		float f1 = Var1.getAsFloat();
		float f2 = Var2.getAsFloat();

		push_float( f2 / f1 );
	}

	// Numerical comparison
	void As2VM::DoEquals(){
		assert(m_Stack.size() >= 2);
		As2Var Var1 = pop();
		As2Var Var2 = pop();

		float f1 = Var1.getAsFloat();
		float f2 = Var2.getAsFloat();

		push_bool( f1 == f2 );
	}

	void As2VM::DoLess(){
		assert(m_Stack.size() >= 2);
		As2Var Var1 = pop();
		As2Var Var2 = pop();

		float f1 = Var1.getAsFloat();
		float f2 = Var2.getAsFloat();

		push_bool( f2 < f1 );
	}

	// Logical operators

	void As2VM::DoAnd(){
		assert(m_Stack.size() >= 2);
		As2Var Var1 = pop();
		As2Var Var2 = pop();

		float f1 = Var1.getAsFloat();
		float f2 = Var2.getAsFloat();

		push_bool( f1 && f2 );
	}

	void As2VM::DoOr(){
		assert(m_Stack.size() >= 2);
		As2Var Var1 = pop();
		As2Var Var2 = pop();

		float f1 = Var1.getAsFloat();
		float f2 = Var2.getAsFloat();

		push_bool( f1 || f2 );
	}

	void As2VM::DoNot(){
		assert(m_Stack.size() >= 1);
		As2Var Var1 = pop();

		float f1 = Var1.getAsFloat();

		push_bool( f1 == 0.0f );
	}

	// String manipulation

	void As2VM::DoStringEquals(){
		assert(m_Stack.size() >= 2);
		As2Var Var1 = pop();
		As2Var Var2 = pop();

		if( Var1.getType() == eString && Var2.getType() == eString && strcmp(Var1.getAsString(), Var2.getAsString()) == 0 ){
			push_bool( true );
		}
		else{
			push_bool( false );
		}
	}

	void As2VM::DoStringLength(){
		assert(m_Stack.size() >= 1);
		As2Var Var1 = pop();

		if( Var1.getType() == eString ){
			push_int( (uint32) strlen( Var1.getAsString() ) );
		}
		else{
			// push_float( 0.0f ); // not sure if I should push 0 if is not a string
		}
	}

	void As2VM::DoStringAdd(){
		assert(m_Stack.size() >= 2);
		char buffer[20] = {0};
		As2Var Var1 = pop();
		As2Var Var2 = pop();
		
		string sz = "";
		// string 1
		if(Var1.getType() == eString){
			sz.append( Var1.getAsString() );
		}
		else if(Var1.getType() == eFloat || Var1.getType() == eDouble ){
			sprintf_s(buffer, "%f" , Var1.getAsFloat() );
			sz.append( buffer );
		}
		else if(Var1.getType() == eInt){
			sprintf_s(buffer, "%d" , Var1.getAsInt() );
			sz.append( buffer );
		}

		// + string 2
		if(Var2.getType() == eString){
			sz.append( Var2.getAsString() );
		}
		else if(Var2.getType() == eFloat || Var2.getType() == eDouble ){
			sprintf_s(buffer, "%f" , Var2.getAsFloat() );
			sz.append( buffer );
		}
		else if(Var2.getType() == eInt){
			sprintf_s(buffer, "%d" , Var2.getAsInt() );
			sz.append( buffer );
		}

		push_NewStr( sz.c_str() );
	}

	void As2VM::DoStringExtract(){
		assert(m_Stack.size() >= 3);
		
		As2Var Var1 = pop();
		As2Var Var2 = pop();
		As2Var Var3 = pop();

		if( Var1.getType() == eInt && Var2.getType() == eInt && Var3.getType() == eString ){
			string str = Var3.getAsString();
			string substr = str.substr( Var1.getAsInt(), Var2.getAsInt() );
			push_NewStr( substr.c_str() );
		}
		else {
			push_str( s_empty_string );
		}
	}

	void As2VM::DoStringLess(){
		assert(m_Stack.size() >= 2);
		
		As2Var Var1 = pop();
		As2Var Var2 = pop();

		if( Var1.getType() == eString && Var2.getType() == eString ){
			push_bool( strcmp( Var1.getAsString(), Var2.getAsString() ) > 0 );
		}
		else {
			push_bool( false );
		}
	}

	void As2VM::DoMBStringLength(){
		assert(m_Stack.size() >= 1);

		assert(0);
		// not supported
		// do anyway the correct push and pop number to avoid stack incoherence
		As2Var Var1 = pop();
		push_int( 0 );
	}

	void As2VM::DoMBStringExtract(){
		assert(m_Stack.size() >= 3);

		assert(0);
		// not supported
		// do anyway the correct push and pop number to avoid stack incoherence
		As2Var Var1 = pop();
		As2Var Var2 = pop();
		As2Var Var3 = pop();

		push_str( s_empty_string );
	}

	// Type conversion

	void As2VM::DoToInteger(){
		assert(m_Stack.size() >= 1);

		As2Var Var1 = pop();
		push_int( Var1.getAsInt() );
	}

	void As2VM::DoCharToAscii(){
		assert(m_Stack.size() >= 1);

		As2Var Var1 = pop();
		if( Var1.getType() == eString ){
			push_int( (int)(Var1.getAsString()[0]) );
		}
		else{
			push_int( 0 );
		}
	}

	void As2VM::DoAsciiToChar(){
		assert(m_Stack.size() >= 1);

		As2Var Var1 = pop();
		if( Var1.getType() == eInt ){
			char str[2] = { (char)Var1.getAsInt() , '\0' };
			push_NewStr( str );
		}
		else{
			push_str( s_empty_string );
		}
	}

	void As2VM::DoMBCharToAscii(){
		assert(m_Stack.size() >= 1);

		assert(0);
		// not supported
		// do anyway the correct push and pop number to avoid stack incoherence
		As2Var Var1 = pop();
		push_int( 0 );
	}
	
	void As2VM::DoMBAsciiToChar(){
		assert(m_Stack.size() >= 1);

		assert(0);
		// not supported
		// do anyway the correct push and pop number to avoid stack incoherence
		As2Var Var1 = pop();
		push_str( s_empty_string );
	}
};